package com.firstsight.micro.homedo.utils;

import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.sun.pdfview.PDFFile;
import com.sun.pdfview.PDFPage;
import com.sun.pdfview.PDFRenderer;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.ByteBuffer;
import java.text.MessageFormat;
import java.util.List;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * @ClassName FileUtils
 * @Author yanshuchun
 * @Date 2021年6月25日08:59:27
 **/
@Slf4j
public class FileUtils {

    public static final String imageTypeDef = "jpg";
    public static final Integer defaultNum = 50;
    public static final String command = "soffice --headless --convert-to pdf {0} --outdir {1}";
    public static final String MARK_URL = "https://wuyou-resource.oss-cn-shanghai.aliyuncs.com/desktop.png";

    /**
     * 线上文件下载
     * @param urlStr 线上地址
     * @return
     */
    public static InputStream downLoadFromUrl(String urlStr) {
        InputStream inputStream = null;
        try {
            URL url = new URL(urlStr);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            // 设置超时间为3秒
            conn.setConnectTimeout(30 * 1000);
            // 防止屏蔽程序抓取而返回403错误
            conn.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)");
            // 得到输入流
            inputStream = conn.getInputStream();
        } catch (Exception e) {
            e.printStackTrace();
//            throw new HomedoException(ResponseEnum.ALI_OSS_FILE_NOT_EXIST);
        }

        return inputStream;
    }

    /**
     * pdf转换成图片
     * <p>
     * 说明：
     * ①pdf文件内若所有pdf页的宽度全部一致，则每10页生成一张图片，按照pdf页面顺序放入 List 中返回
     * ②pdf文件内若有宽度不一致的pdf页，则每页会被生成一张图片，按照pdf页面顺序放入 List 中返回
     * ③pdf转换方法出现异常，会清空结果集list中的所有已经转化好的图片地址，把原地址放进去。
     *
     * @param fromUrl pdf 网络连接地址
     * @param imgType 即将装换成的图片的类型
     * @return imgUrl 转换成功并上传 OSS 后的路径
     */
    public static List<String> pdf2Image(String fromUrl, String imgType, Boolean markOrNot) {
        imgType = imgType == null ? imageTypeDef : imgType;
        List<BufferedImage> imageTempList;
        List<String> res = new ArrayList<>();
        try {
            log.info("读取网络数据{}", fromUrl);
            byte[] byt = toByteArray(downLoadFromUrl(fromUrl));
            ByteBuffer buf = ByteBuffer.allocate(byt.length);
            buf.put(byt);
            PDFFile pdfFile = new PDFFile(buf);
            int numPages = pdfFile.getNumPages();
            log.info("pdf页码{}", numPages);
            BufferedImage[] bufferedImages = new BufferedImage[numPages];
            List<Integer> list = new ArrayList<>();
            for (int i = 0; i < numPages; i++) {
                list.add(i);
            }
            List<Integer> pageNumList = Collections.synchronizedList(list);
            log.info("将pdf文件对象中的数据读出到 imageTemps(数组)");
            pageNumList.parallelStream().forEach(pageNum -> {
                try {
                    getbufferedImage(pdfFile, pageNum, bufferedImages);
                } catch (Exception e) {
                    log.info(e.getMessage());
                }
            });
            imageTempList = Arrays.asList(bufferedImages);
            log.info("imageTemps(数组) 中的图片信息读取到图片中");
            Map<Integer, ArrayList<BufferedImage>> resMap = new HashMap<>();
            log.info("按照每页的宽度，页码分组");
            imageGroup(imageTempList, resMap);
            String[] resMapArray = new String[resMap.size()];
            ThreadFactory fileDeal = new ThreadFactoryBuilder()
                    .setNameFormat("PDF2IMAGE-线程池-pool-%d").build();
            ExecutorService executorService = new ThreadPoolExecutor(resMap.size(), resMap.size(),
                    0L, TimeUnit.MILLISECONDS,
                    new LinkedBlockingQueue<>(512), fileDeal, new ThreadPoolExecutor.AbortPolicy());
            String finalImgType = imgType;
            resMap.forEach((key, imageTempArrayList) -> executorService.execute(() -> {
                imageTogether(key, imageTempArrayList, finalImgType, fromUrl, resMapArray, markOrNot);
            }));
            executorService.shutdown();
            executorService.awaitTermination(120, TimeUnit.SECONDS);
            res = Arrays.asList(resMapArray);
        } catch (Exception e) {
            log.error("转换pdf异常：{},{}", fromUrl, e.getMessage());
        }
        return res;
    }

    /**
     * 图片拼接在一起
     *
     * @param index
     * @param imageTempArrayList
     * @param imgType
     * @param fromUrl
     * @param resMapArray
     */
    static void imageTogether(int index, ArrayList<BufferedImage> imageTempArrayList, String imgType, String fromUrl, String[] resMapArray, Boolean markOrNot) {
        List<BufferedImage> imageTemps = Collections.synchronizedList(imageTempArrayList);
        Integer height = imageTemps.parallelStream().collect(Collectors.summingInt(BufferedImage::getHeight));
        Integer width = imageTemps.get(0).getWidth();
        BufferedImage imageResult = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        AtomicInteger y = new AtomicInteger();
        imageTemps.stream().forEach(bufferedImage -> {
            int[] imgRGBArray = new int[width * bufferedImage.getHeight()];
            bufferedImage.getRGB(0, 0,
                    width, bufferedImage.getHeight(), imgRGBArray, 0, width);
            imageResult.setRGB(0, y.get(), width, bufferedImage.getHeight(), imgRGBArray, 0, width);
            y.addAndGet(bufferedImage.getHeight());
        });
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        try {
            ImageIO.write(markOrNot ? mark(imageResult) : imageResult, imgType, os);
        } catch (IOException e) {
            e.printStackTrace();
        }
        resMapArray[index] = AliyunOssUtil.uploadFile(getNameFromUrl(fromUrl, imgType, index), new ByteArrayInputStream(os.toByteArray()));
    }

    static void imageGroup(List<BufferedImage> imageTempList, Map<Integer, ArrayList<BufferedImage>> resMap) {
        ArrayList<BufferedImage> resList = new ArrayList();
        int index = 0;
        for (int i = 0; i < imageTempList.size(); i++) {
            BufferedImage bufferedImage = imageTempList.get(i);
            if (i == 0) {
                resList.add(bufferedImage);
                if (imageTempList.size() == 1) {
                    resMap.put(index, resList);
                }
            } else {
                if (imageTempList.get(i - 1).getWidth() == bufferedImage.getWidth() && i % defaultNum != 0) {
                    resList.add(bufferedImage);
                    if (i == imageTempList.size() - 1) {
                        resMap.put(index++, resList);
                    }
                } else {
                    resMap.put(index++, resList);
                    resList = new ArrayList<>();
                    resList.add(bufferedImage);
                }
            }
        }
    }


    /**
     * @param pdfFile        pdf文件对象
     * @param pageNum        页码
     * @param bufferedImages 图片对象数组
     * @throws InterruptedException
     */
    static void getbufferedImage(PDFFile pdfFile, Integer pageNum, BufferedImage[] bufferedImages) throws InterruptedException, IOException {
        PDFPage page = pdfFile.getPage(pageNum + 1);
        int width = (int) page.getWidth();
        int height = (int) page.getHeight();
        BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
        Graphics2D g2 = img.createGraphics();
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        PDFRenderer renderer = new PDFRenderer(page, g2,
                new Rectangle(0, 0, width, height), null, Color.white);
        page.waitForFinish();
        renderer.run();
        g2.dispose();
        bufferedImages[pageNum] = img;
    }


    public static String getNameFromUrl(String fromUrl, String imgType, int j) {
        String imageName = fromUrl.substring(fromUrl.lastIndexOf("/") + 1);
        if (StringUtils.isNotEmpty(imageName)) {
            int i = imageName.lastIndexOf(".");
            if (i == -1) {
                imageName = imageName + "_" + j + "." + imgType;
            } else {
                imageName = imageName.substring(0, i) + "_" + j + "." + imgType;
            }
        } else {
            imageName = System.currentTimeMillis() + "_" + j;
        }
        return imageName;
    }

    public static byte[] toByteArray(InputStream in) throws IOException {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024 * 20];
        int n = 0;
        while ((n = in.read(buffer)) != -1) {
            out.write(buffer, 0, n);
        }
        return out.toByteArray();
    }

    /**
     * 接近底层的加水印，性能好点
     *
     * @param imageBuffer
     * @return
     * @throws IOException
     */
    public static BufferedImage mark(BufferedImage imageBuffer) {
        BufferedImage bufImg = null;
        InputStream inputStream = FileUtils.downLoadFromUrl(MARK_URL);
        try {
            BufferedImage image = ImageIO.read(inputStream);
            // 读取原图片信息
            int srcImgWidth = imageBuffer.getWidth(null);
            int srcImgHeight = imageBuffer.getHeight(null);
            // 加水印
            bufImg = new BufferedImage(srcImgWidth, srcImgHeight, BufferedImage.TYPE_INT_RGB);
            Graphics2D g = bufImg.createGraphics();
            g.drawImage(imageBuffer, 0, 0, srcImgWidth, srcImgHeight, null);
            g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, 0.7F));
            Random random = new Random();
            for (int i = 0; i < srcImgHeight; ) {
                for (int j = 0; j < srcImgWidth; ) {
                    g.drawImage(image, null, j, i); // 绘制文本
                    j += 500 + random.nextInt(100);
                }
                i += 100 + random.nextInt(100);
            }
            g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER));
            g.dispose();
            bufImg.flush();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return bufImg;
    }

    //*******************************************************************************************

    /**
     * PPT文件转PDF
     * <p>
     * 测试数据 http://wuyou-resource.oss-cn-shanghai.aliyuncs.com/test.pptx
     */
    public static List<String> office2Image(String fileUrl, Boolean markOrNot) {
        List<String> urls = new ArrayList<>();
        String nameFromUrl = getNameFromUrl(fileUrl);
        if (StringUtils.isEmpty(nameFromUrl)) {
            log.info("获取文件名称失败，直接返回");
            return urls;
        }
        String tempPath = System.getProperty("user.dir");
        log.info("获取存放临时文件的地址：{}", tempPath);
        try {
            if (exec(MessageFormat.format(command, fileUrl, tempPath))) {
//            执行成功
                File file = new File(tempPath + File.separator + nameFromUrl);
                if (file.exists()) {
                    urls = pdf2Image(AliyunOssUtil.uploadFile(nameFromUrl, file), null, markOrNot);
                    log.info("转化完成。");
                    log.info("删除本地缓存文件 {}", file.delete());
                }
            } else {
//            执行失败
                log.info("{}，转换失败！！！", fileUrl);
            }
        } catch (Exception e) {
            log.error(e.getMessage());
        }
        return urls;
    }

    public static String getNameFromUrl(String url) {
        String substring = null;
        try {
            substring = url.substring(url.lastIndexOf("/") + 1, url.lastIndexOf("."));
        } catch (Exception e) {
            log.error(e.getMessage());
        } finally {
            if (StringUtils.isNotEmpty(substring)) {
                substring += ".pdf";
            }
            return substring;
        }
    }

    public static boolean exec(String command) throws Exception {
        boolean res = false;
        log.info("执行命令行：{}", command);
        Process process = Runtime.getRuntime().exec(command);
        int exitStatus = process.waitFor();
        if (exitStatus != 0) {
            log.error("exec cmd exitStatus {}", exitStatus);
        } else {
            res = true;
            log.debug("exec cmd exitStatus {}", exitStatus);
        }
        if (ObjectUtils.isNotEmpty(process)) {
            process.destroy(); // 销毁子进程
        }
        log.info("执行结果 {}", res);
        return res;
    }

    public static String addMarkToImage(String url) {
        try {
            String type = url.substring(url.lastIndexOf(".") + 1);
            ByteArrayOutputStream os = new ByteArrayOutputStream();
            ImageIO.write(mark(ImageIO.read(new URL(url))), type, os);
            return AliyunOssUtil.uploadFile(url.substring(url.lastIndexOf("/") + 1, url.lastIndexOf(".") + 1) + type, new ByteArrayInputStream(os.toByteArray()));
        } catch (IOException e) {
//            throw new BizException(BizErrorCodeEnum.FILE_NOT_SUPPORT);
        }
        return null;
    }

}
